/**
 * Licensed under the MIT License
 *
 * Copyright (c) 2010 Alexandre Croiseaux
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
package marcel.display.gui.components
{
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.FocusEvent;
	import flash.events.KeyboardEvent;
	import flash.filters.GlowFilter;
	import flash.geom.Point;
	import flash.text.TextFieldType;
	import marcel.display.gui.form.IFormComponent;
	import marcel.display.gui.texts.CoreField;
	import marcel.display.View;
	import marcel.external.browser.WModeManager;
	import swc.form.InputTextAsset;

	/**
	 * A Checkbox component class to use in forms.
	 * The graphics are editable inside the form.fla file
	 * @author Alexandre Croiseaux
	 */
	public class InputText extends View implements IFormComponent
	{
		//--------------------------------------------------------------------------
		//
		//  Private vars
		//
		//--------------------------------------------------------------------------
		/**
		 * @private
		 */
		protected var _gfx : InputTextAsset;
		/**
		 * @private
		 */
		protected var _mcBg : MovieClip;
		/**
		 * @private
		 */
		protected var _oMargin : Point;
		/**
		 * @private
		 */
		protected var _tfInput : CoreField;
		/**
		 * @private
		 */
		protected var _bUseWModeManager : Boolean;
		/**
		 * @private
		 */
		protected var _defaultValue:String;
		/**
		 * @private
		 */
		protected var _displayAsPassword:Boolean;
		
		
		//--------------------------------------------------------------------------
		//
		//  Constructor
		//
		//--------------------------------------------------------------------------
		/**
		 * Constructs a new InputText instance
		 * @param	classStyle	the style of text to apply
		 * @param	cssId	the css id to get the style class from
		 */
		public function InputText(classStyle:String = null, cssId:String = null)
		{
			_gfx = new InputTextAsset();
			addChild(_gfx);
			
			_mcBg = _gfx.mcBg;
			_oMargin = new Point(_gfx.mcMarginGuide.x, _gfx.mcMarginGuide.y);
			
			_tfInput = new CoreField(_gfx.mcMarginGuide.x, _gfx.mcMarginGuide.y, _gfx.width - (_gfx.mcMarginGuide.x*2), _gfx.height - _gfx.mcMarginGuide.y);
			_tfInput.selectable = true;
			_tfInput.type = TextFieldType.INPUT;
			addChild(_tfInput);
			
			if (classStyle) setStyle(classStyle, cssId);
			
			_setInteractivity();
		}
		
		
		//--------------------------------------------------------------------------
		//
		//  Public methods
		//
		//--------------------------------------------------------------------------
		/**
		 * Returns the text if the current InputText. Casted as * to implement IFormComponent interface
		 * @return	the text if the current InputText
		 */
		public function getValue():*
		{
			return _tfInput.text;
		}
		
		/**
		 * Sets the text of the current InputText and updates the graphics if needed.
		 * @param	oValue	a text string
		 */
		public function setValue(oValue:*) : void
		{
			_tfInput.text = oValue.toString();
			refreshView();
		}
		
		/**
		 * Sets the css style of the inner textfield.
		 * @param	classStyle	the style of text to apply
		 * @param	cssId	the css id to get the style class from
		 */
		public function setStyle(classStyle:String, cssId:String = null):void
		{
			_tfInput.setCSSStyle(classStyle, cssId);
			refreshView();
		}
		
		/**
		 * Access to the inner CoreField instance.
		 * @return	a CoreField instance
		 */
		public function getTextField() : CoreField {
			return _tfInput;
		}
		
		/**
		 * Access to the background graphics
		 * @return	an DisplayObject instance
		 */
		public function getBackground() : DisplayObject {
			return _mcBg;
		}
		
		/**
		 * Indicates the width of the display object, in pixels.
		 */
		override public function set width(value:Number):void
		{
			_mcBg.width = value;
			_tfInput.width = value - (_gfx.mcMarginGuide.x * 2);
		}
		
		/**
		 * Indicates the height of the display object, in pixels.
		 */
		override public function set height(value:Number):void
		{
			_mcBg.height = value;
			_tfInput.height = value - _gfx.mcMarginGuide.y;
		}
		
		/**
		 * Indicates if the input text must fix the WMode bug mith special characters
		 */
		public function set fixWMode(value:Boolean):void
		{
			_bUseWModeManager = value;
		}
		
		/**
		 * Indicates the set of characters that a user can enter into the text field.
		 */
		public function set restrict(value:String):void
		{
			_tfInput.restrict = value;
		}
		
		/**
		 * The maximum number of characters that the text field can contain, as entered by a user.
		 */
		public function set maxChars(value:int):void
		{
			_tfInput.maxChars = value;
		}
		
		/**
		 * Specifies whether the text field is a password text field.
		 */
		public function set displayAsPassword(value:Boolean):void
		{
			_displayAsPassword = value;
			_tfInput.displayAsPassword = value && getValue() != _defaultValue;
		}
		
		/**
		 * sets the default text for the current input text. If the text is empty, this text is displayed again on the focus out.
		 */
		public function set defaultValue(value:String):void
		{
			if (_tfInput.length == 0 || _tfInput.text == _defaultValue)
			{
				_tfInput.displayAsPassword = false;
				setValue(value);
			}
			_defaultValue = value;
		}
		
		/**
		 * Specifies the tab ordering of objects in a SWF file.
		 */
		override public function get tabIndex():int { return _tfInput.tabIndex; }
		override public function set tabIndex(value:int):void
		{
			_tfInput.tabIndex = value;
		}
		
		/**
		 * Displays the current component as having an error or not. This method is automatically called by the Form class if form.useHighlight is set to true.
		 * @param	hasError	Indicates if there is an error or not.
		 */
		public function displayError(hasError:Boolean):void
		{
			_mcBg.filters = hasError ? [new GlowFilter(0xFF0000, 1, 2, 2, 2, 3, true, false)] : null;
		}
		
		
		//--------------------------------------------------------------------------
		//
		//  Private methods
		//
		//--------------------------------------------------------------------------
		/**
		 * @private
		 */
		protected function refreshView() : void {
			_mcBg.width = _tfInput.width + _oMargin.x * 2;
			_mcBg.height = _tfInput.height + _oMargin.y;
		}
		
		/**
		 * @private
		 */
		protected function _setInteractivity() : void {
			_tfInput.addEventListener(FocusEvent.FOCUS_IN, _onFocus);
			_tfInput.addEventListener(FocusEvent.FOCUS_OUT, _onKillFocus);
			_tfInput.addEventListener(Event.CHANGE, _onChange);
			_tfInput.addEventListener(KeyboardEvent.KEY_DOWN, _onKeyDown);
		}
		
		private function _fillInput(sValue:String) : void
		{
			if (sValue == null)
			{
				dispatchEvent(new FocusEvent(FocusEvent.FOCUS_OUT));
				return;
			}
			
			// check input validity
			var bIsValid : Boolean = true;
			if (sValue.length > 0) bIsValid = _isStringMatchingRestrict(sValue, _tfInput.restrict);
			if (_tfInput.maxChars > 0) bIsValid = bIsValid && (sValue.length <= _tfInput.maxChars);
			
			// if ok => set, else remove entered char from html textfield
			if (bIsValid) setValue(sValue);
			else WModeManager.getInstance().removeLastChar();
			
			// go back to swf
			WModeManager.getInstance().giveFocusToSwf();
			stage.focus = _tfInput;
			var oSel : Point = WModeManager.getInstance().getJSSelection();
			_tfInput.setSelection(oSel.x, oSel.y);
		}
		
		private function _isStringMatchingRestrict(s : String, sRestrict : String = null) : Boolean {
			if(!sRestrict) return true;
			var a : Array = sRestrict.split('^');
			var sInclude : String = a[0];
			var sExclude : String = a[1];
			
			var re : RegExp;
			
			if(sExclude && sExclude.length > 0) {
				re = new RegExp('[^' + sExclude + ']*', 'g');
				if(s.match(re)[0] !== s) return false;
			}
			
			if(sInclude && sInclude.length > 0) {
				re = new RegExp('[' + sInclude + ']*', 'g');
				if(s.match(re)[0] !== s) return false;
			}
			
			return true;
		}
		
		//--------------------------------------------------------------------------
		//
		//  Events
		//
		//--------------------------------------------------------------------------
		private function _onKeyDown(event:KeyboardEvent):void
		{
			// force un focus HTML sur les alt/shift/ctrl sinon firefox ne voit pas la touche enfoncée
			if (event.altKey || event.ctrlKey || event.shiftKey) _onChange(event);
		}
		
		private function _onChange(event:Event):void
		{
			if (_bUseWModeManager && WModeManager.getInstance().isOn)
			{
				WModeManager.getInstance().giveFocusToHtml(_tfInput.text, _tfInput.selectionBeginIndex, _tfInput.selectionEndIndex);
				WModeManager.getInstance().callback = _fillInput;
			}
		}
		
		private function _onFocus(e:FocusEvent):void
		{
			var prevCallback:Function = WModeManager.getInstance().callback;
			
			if (_bUseWModeManager && WModeManager.getInstance().isOn)
			{
				WModeManager.getInstance().callback = _fillInput;
			}
			else
			{
				WModeManager.getInstance().callback = null;
			}
			
			if (prevCallback != null) prevCallback(null); // dispatch focus out on previous input
			
			if (_defaultValue && _tfInput.text == _defaultValue)
			{
				setValue("");
				_tfInput.displayAsPassword = _displayAsPassword;
			}
		}
		
		private function _onKillFocus(e:FocusEvent):void
		{
			if (WModeManager.getInstance().callback == _fillInput) e.stopPropagation(); // the focus out is due to html field focus in, so do not notify to form item
			
			if (!WModeManager.getInstance().hasFocusHtml())
			{
				if (_tfInput.length == 0 && _defaultValue)
				{
					setValue(_defaultValue);
					_tfInput.displayAsPassword = false;
				}
			}
		}
	}
}
